import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'linphone_flutter_platform_interface.dart';
import 'linphone_chat_message.dart';
import 'dart:async';
import 'dart:io';

enum CallState {
  IncomingReceived,
  Connected,
  Released,
  OutgoingInit,
  OutgoingProgress,
  OutgoingRinging,
  StreamsRunning,
  Paused,
  PausedByRemote,
  Updating,
  UpdatedByRemote,
  Error
}

abstract class LinphoneListener {
  void connected(bool success) {
  }

  void onCallStateChanged(CallState state) {

  }

  void onMessageReceived(String message) {

  }
}

/// An implementation of [LinphoneFlutterPlatform] that uses method channels.
class MethodChannelLinphoneFlutter extends LinphoneFlutterPlatform {
  /// The method channel used to interact with the native platform.
  @visibleForTesting
  final methodChannel = const MethodChannel('linphone_flutter');
  final eventChannel = const EventChannel('linphone_flutter/core');
  LinphoneListener? listener;
  
  @override
  Future<String?> getLinphoneVersion() async {
    final version = await methodChannel.invokeMethod<String>(
        'getLinphoneVersion');
    return version;
  }

  @override
  Future<void> login(String username, String password, String domain) async {
    Map arguments = {
      "username": username,
      "password": password,
      "domain": domain
    };
    await methodChannel.invokeMethod<Map<String, dynamic>>('login', arguments);
  }

  void setCoreListener(LinphoneListener listener) {
    this.listener = listener;
    eventChannel?.receiveBroadcastStream().listen((dynamic res) {
      if (res["type"] == "connected") {
        listener?.connected(res["result"]);
      }
      else if (res["type"] == "CallState") {
        switch (res["result"]) {
          case "IncomingReceived":
            listener?.onCallStateChanged(CallState.IncomingReceived);
            break;
          case "Connected":
            listener?.onCallStateChanged(CallState.Connected);
            break;
          case "Released":
            listener?.onCallStateChanged(CallState.Released);
            break;
          case "OutgoingInit":
            listener?.onCallStateChanged(CallState.OutgoingInit);
            break;
          case "OutgoingProgress":
            listener?.onCallStateChanged(CallState.OutgoingProgress);
            break;
          case "OutgoingRinging":
            listener?.onCallStateChanged(CallState.OutgoingRinging);
            break;
          case "StreamsRunning":
            listener?.onCallStateChanged(CallState.StreamsRunning);
            break;
          case "Paused":
            listener?.onCallStateChanged(CallState.Paused);
            break;
          case "PausedByRemote":
            listener?.onCallStateChanged(CallState.PausedByRemote);
            break;
          case "Updating":
            listener?.onCallStateChanged(CallState.Updating);
            break;
          case "UpdatedByRemote":
            listener?.onCallStateChanged(CallState.UpdatedByRemote);
            break;
          case "Error":
            listener?.onCallStateChanged(CallState.Error);
            break;
        }
        if(Platform.isAndroid) {
          listener?.onMessageReceived(res["message"]);
        }
      } else if (res["type"] == "onMessageReceived") {
        listener?.onMessageReceived(res["result"]);
      }
    }, onError: (dynamic error) {
      print('Received error: ${error.message}');
    }, cancelOnError: true);
    
  }
  
  @override
  Future<void> unregister() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('unregister');
  }

  @override
  Future<void> delete() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('delete');
  }

  @override
  Future<void> terminate() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('terminate');
  }

  @override
  Future<void> accept() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('accept');
  }

  @override
  Future<void> outgoingCall(String? remoteSipUri) async {
    Map arguments = {
      "remoteSipUri": remoteSipUri
    };
    await methodChannel.invokeMethod<Map<String, dynamic>>(
        'outgoingCall', arguments);
  }

  @override
  Future<void> hangUp() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('hangUp');
  }

  @override
  Future<void> transferTo(String? remoteSipUri) async {
    Map arguments = {
      "remoteSipUri": remoteSipUri
    };
    await methodChannel.invokeMethod<Map<String, dynamic>>(
        'transferTo', arguments);
  }

  @override
  Future<void> pauseOrResume() async {
    await methodChannel.invokeMethod<Map<String, dynamic>>('pauseOrResume');
  }

  @override
  Future<bool> micEnabled() async {
    return await methodChannel.invokeMethod<bool>('micEnabled') as bool;
  }

  @override
  Future<bool> toggleSpeaker() async {
    return await methodChannel.invokeMethod<bool>('toggleSpeaker') as bool;
  }

  @override
  Future<String?> remoteAddressUri() async {
    return await methodChannel.invokeMethod<String>('remoteAddressUri');
  }

  @override
  Future<String?> sendMessage(LinphoneChatMessage chatMessage) async {
    Map arguments = {
      "remoteSipUri": chatMessage.to,
      "message": chatMessage.message,
      "localTime": chatMessage.localTime.toString()
    };
    return await methodChannel.invokeMethod<String>('sendMessage', arguments);
  }
  
  @override
  Future<bool?> requestPermissions() async {
    return await methodChannel.invokeMethod<bool>('requestPermissions');
  }
}
